/**
 * 
 */
package com.cipe.cmrs.validator;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;

import com.cipe.cmrs.bean.PaymentRequestBean;
import com.cipe.cmrs.bean.PaymentRequestLineItemBean;
import com.cipe.cmrs.exception.QueryException;
import com.cipe.cmrs.helper.PaymentHelper;
import com.cipe.cmrs.model.Cipeproject;
import com.cipe.cmrs.service.CipeprojectService;
import com.cipe.cmrs.service.PaymentrequestService;
import com.cipe.cmrs.service.PaymentrequestlineitemService;
import com.cipe.cmrs.util.DateUtil2;

/**
 * @author Incepio
 * 
 */
@Component
public class PaymentRequestValidator extends AbstractValidator {
	@Autowired
	private CipeprojectService projectService;
	@Autowired
	private PaymentrequestlineitemService lineItemService;
	@Autowired
	private PaymentHelper paymentHelper;
	@Autowired
	private PaymentrequestService paymentrequestService;

	public void validate(Errors errors, PaymentRequestBean bean) {
		StringBuilder sb = new StringBuilder();
		if (!paymentHelper.hasData(bean.getLineItemBeans())) {
			errors.reject(
					"required.paymentrequest",
					"A Payment Request must have at least 1 line item created/associated. Please select an Account, a Project ID and an Amount for a Line Item");
		} else if (bean.getPaymentTypeId() != null
				&& bean.getPaymentTypeId() == Integer
						.valueOf(getText("id.paymenttype.reclassliquidation"))
				&& BigDecimal.ZERO.compareTo(paymentHelper.getTotalAmount(bean
						.getLineItemBeans())) != 0) {
			errors.reject(
					"error.notzero.balance.payment.reclass",
					"For payment requests of type 'Reclass/Liquidations', the total balance of all line items MUST be EQUAL to 0.");
		} else if (StringUtils.isNotBlank(bean.getInvoicenumber())
				&& bean.getInvoiceRequestDate() != null
				&& paymentrequestService.isExisting(bean)) {
			Object[] args = new Object[] {
					bean.getInvoicenumber(),
					DateUtil2.INSTANCE.formatDate(DateUtil2.INSTANCE
							.getSodDate(bean.getInvoiceRequestDate())),
					bean.getPayeeDesc(),
					bean.getAmount() != null ? bean.getAmount().toPlainString()
							: "" };
			errors.reject(
					"error.payment.duplicate.entry",
					args,
					String.format(
							"Payment with invoice number %s, invoice date %s, payee %s, amount %s already exists.",
							args));
		} else {
			boolean hasBlankAcctCodeOrProjEntry = false;
			Map<Integer, List<PaymentRequestLineItemBean>> map = new HashMap<Integer, List<PaymentRequestLineItemBean>>();
			for (PaymentRequestLineItemBean lineBean : bean.getLineItemBeans()) {
				BigDecimal amount = lineBean.getAmount();
				Integer acId = lineBean.getAccountCodeId();
				Integer ciId = lineBean.getCipeProjectId();

				if (BigDecimal.ZERO.compareTo(amount) != 0) {
					if ((acId == null || acId < 1)
							&& (ciId == null || ciId < 1)) {
						hasBlankAcctCodeOrProjEntry = true;
						sb.append("Account and Project ID must be created/associated with line item . Please select an Account and a Project ID for a Line Item.");
						break;
					} else if (ciId == null || ciId < 1) {
						hasBlankAcctCodeOrProjEntry = true;
						sb.append("Project ID must be created/associated with line item . Please select a Project ID for a Line Item.");
						break;
					} else if ((acId == null || acId < 1)) {
						hasBlankAcctCodeOrProjEntry = true;
						sb.append("Account must be created/associated with line item . Please select an Account for a Line Item.");
						break;
					} else {
						List<PaymentRequestLineItemBean> list = map.get(ciId);
						if (list == null) {
							list = new ArrayList<PaymentRequestLineItemBean>();
							map.put(ciId, list);
						}
						list.add(lineBean);
					}
				}
			}
			if ((hasBlankAcctCodeOrProjEntry || map.size() < 1) &&  bean.getPaymentTypeId() !=5) {
				if (map.size() < 1) {
					errors.reject("error.payment.empty.projectoraccount",
							"Line Item Amount is zero. Please select a non-zero Amount for a Line Item.");
				} else {
					errors.reject("error.payment.empty.projectoraccount",
							sb.toString());
				}
			} else {
				try {
					List<String> columns = new ArrayList<String>();
					columns.add("id");
					columns.add("managementFunds");
					columns.add("partnerGrantFunds");
					columns.add("override");
					columns.add("statusoverride");
					columns.add("projectTitle");
					columns.add("cipeprojectstatus.id");
					columns.add("cipeprojectstatus.displayName");
					columns.add("projectEndDate");
					columns.add("cipeprojecttype.displayName");
					columns.add("projectTitle");
					columns.add("projectID");
					columns.add("projectTitle");
					for (Integer projId : map.keySet()) {
						Cipeproject project = projectService.getCipeProject(
								projId, columns);
						BigDecimal remainingFunds = getRemainingFunds(
								map.get(projId), project, bean);
						paymentHelper.checkForError(errors, remainingFunds,
								project);
					}
				} catch (QueryException e) {
					log.error(getText("error.transaction"), e);
					errors.reject("error.transaction");
				}
			}
		}
	}

	private BigDecimal getRemainingFunds(List<PaymentRequestLineItemBean> list,
			Cipeproject project, PaymentRequestBean bean) {
		BigDecimal newTotalAmounts = paymentHelper.getTotalAmount(list);
		log.debug("New Total Amounts: " + newTotalAmounts);
		BigDecimal deductedAmounts = lineItemService.getProjectTotalAmount(
				bean.getId(), project.getId());
		log.debug("Deducted Amounts: " + deductedAmounts);
		BigDecimal remainingFunds = BigDecimal.ZERO;
		if (project.getManagementFunds() != null) {
			remainingFunds = project.getManagementFunds();
		}
		if (project.getPartnerGrantFunds() != null) {
			remainingFunds = remainingFunds.add(project.getPartnerGrantFunds());
		}
		remainingFunds = remainingFunds.subtract(deductedAmounts);
		log.debug("Initial Remaining Funds: " + remainingFunds);
		remainingFunds = remainingFunds.subtract(newTotalAmounts);
		log.debug("  Final Remaining Funds: " + remainingFunds);
		return remainingFunds;
	}
}
